/* hal_dma.c */
#include "fsl_common.h"
#include "hal_dma.h"


/* DMA描述符列表的起始地址要求512字节对齐，每个DMA描述符占用16个字节，总共有30个描述符 */
volatile uint8_t gDmaDescriptorBuffer[512 + 16 * DMA_CHANNEL_COUNT];
DMA_TransferDescriptor_T *gDmaDescriptorTableBase;


void DMA_Init(DMA_Type *base)
{
    /* 启用DMA控制器 */
    base->CTRL = DMA_CTRL_ENABLE_MASK;

    /* 设定DMA描述符列表的地址。DMA控制器通过基址和偏移量找到对应通道的描述符通道 */
#if 0
    gDmaDescriptorTableBase = (DMA_TransferDescriptor_T *)( 0xFFFFFE00
            & ((uint32_t)(gDmaDescriptorBuffer)+sizeof(gDmaDescriptorBuffer)-1U)
            );
    base->SRAMBASE = (uint32_t)gDmaDescriptorTableBase;
#endif
    base->SRAMBASE = 512U + (uint32_t)gDmaDescriptorBuffer;
}

void DMA_Deinit(DMA_Type *base)
{
    /* 停用并复位DMA控制器 */
    base->CTRL = 0U;
}

/* 查看全局中断标志位，多个通道中，只有有任何一个标志置位，将触发此处标志位。
 * 返回标志位可在DMA_GlobalInterruptFlags_T中查阅。
 * 此函数返回值参考DMA_GlobalInterruptFlags_T指定。
 */
uint32_t DMA_GetGlobalInterruptStatusFlags(DMA_Type *base)
{
    return base->INTSTAT;
}

/* 启用DMA通道。
 * 只用启用DMA通道，才能使用该通道传输数据。
 * 当禁用通道时，该通道的外部触发接口将被关闭，不能接收新的触发信号，但已经缓存的任务仍能够继续完成。
 */
void DMA_EnableChannels(DMA_Type *base, uint32_t channelMask, bool enable)
{
    if (enable)
    {
        base->COMMON[0].ENABLESET = channelMask;
    }
    else
    {
        base->COMMON[0].ENABLECLR = channelMask;
    }
}

/* 查看当前已经开始的和正在传数或者即将传数的通道，以掩码相或的值返回。
 * 正在工作中的通道不能更改配置，但是可以通过Abort功能终止传输活动
 * 当前某个通道仍有传输描述符可以消耗，则当前处于Active状态
 */
uint32_t DMA_GetActiveChannels(DMA_Type *base)
{
    return base->COMMON[0].ACTIVE;
}

/* Busy表示当前DMA通道内部除了传输数据之外，还有一些其它的收尾工作在进行。
 * DMA传输控制器同CPU类似，实际上也是一个独立的逻辑系统，在搬运数据的前后还需要一些辅助工作需要完成，
 * 但是这些工作没有暴露给用户。因此，当传输完成后，若是需要关闭某个传输通道，不仅需要等传输完成，还要
 * 确保相应的收尾工作已经完成，之后才能安全地关闭通道。 Busy标志位就是表示这些DMA的后台工作是否还在继
 * 续。之后当Busy标志位清零之后才能安全地关闭通道。
 */
uint32_t DMA_GetBusyChannels(DMA_Type *base)
{
    return base->COMMON[0].BUSY;
}

/* 查看当前发生错误的通道，同时也是错误中断的标志位。
*/
uint32_t DMA_GetErrorChannels(DMA_Type *base)
{
    return base->COMMON[0].ERRINT;
}

void DMA_ClearErrorChannels(DMA_Type *base, uint32_t channelMask)
{
    base->COMMON[0].ERRINT = channelMask;
}

/* 此处是DMA通道各自中断的总开关，这个开关管理的不仅仅是传输完成中断，
 * 还有错误中断等等，在不同的工作模式下，中断的实际也不尽相同 */
void DMA_EnableChannelsInterrupt(DMA_Type *base, uint32_t channelMask, bool enable)
{
    if (enable)
    {
        base->COMMON[0].INTENSET = channelMask;
    }
    else
    {
        base->COMMON[0].INTENCLR = channelMask;
    }
}

/* 查看标志的API
 * 仅表示当传输完成后声明IntA的通道，类似和还有IntB。
 */
uint32_t DMA_GetChannelsOnInterruptA(DMA_Type *base)
{
    return base->COMMON[0].INTA;
}

void DMA_ClearFlagsOnInterruptA(DMA_Type *base, uint32_t channelMask)
{
    base->COMMON[0].INTA = channelMask;
}

uint32_t DMA_GetChannelsOnInterruptB(DMA_Type *base)
{
    return base->COMMON[0].INTB;
}

void DMA_ClearFlagsOnInterruptB(DMA_Type *base, uint32_t channelMask)
{
    base->COMMON[0].INTB = channelMask;
}

/* DMA控制器在消化DMA传输描述符之前，在硬件上会自动验证软件配置的有效性，
 * 若是可以安全地被执行，则自动启动传输；若是硬件上任务存在风险，将暂停传输。
 * 此时，若在此处强制忽略硬件的验证并立即执行传输，则可调用此函数作为通行证。
 *　注意：
 * 1. 在每次遇到验证问题时，都需要调用一次本函数，即此处的设定并不是永久的
 * 2. 此函数可以同时设定多个通道，在每个通道自己所属的配置寄存器中，也有各自单独的VALID位可以单独设定
 */
void DMA_DoBypassAutoValidsOfTransferSettingCmd(DMA_Type *base, uint32_t channelMask)
{
    base->COMMON[0].SETVALID = channelMask;
}


/* If a channel is configured with the SWTRIG bit equal to 0, the channel can be later
 * triggered either by hardware or software. Software triggering is accomplished by writing a
 * 1 to the appropriate bit in the SETTRIG register.
 * 在每个通道的配置中，XFERCFGn[CTRTRIG]，还可以设定在单个传输描述符耗尽之后可自动关闭外部触发开关。
 */
void DMA_DoSoftTriggerCmd(DMA_Type *base, uint32_t channelMask)
{
    base->COMMON[0].SETTRIG = channelMask;
}

/* 这里实际上是关闭DMA通道的输出。
* 正确关闭DMA通道的流程是：
* 1. 先关闭DMA通道(的input)
* 2. 等待通道不再busy(on-going)
* 3. 最终关闭DMA通道(的output)
*/
void DMA_DoAbortChannels(DMA_Type *base, uint32_t channelMask)
{
    base->COMMON[0].ABORT = channelMask;
}

/* 配置通道硬件
 */
void DMA_SetChannelConfig(DMA_Type *base, uint32_t channelIndex, DMA_ChannelConfig_T *config)
{
    uint32_t tmp32;

    /* CHANNEL_CFG. */
    tmp32 = (config->EnablePeripheralRequest ? DMA_CHANNEL_CFG_PERIPHREQEN_MASK:0U)
          | (config->EnableHardwareTrigger?DMA_CHANNEL_CFG_HWTRIGEN_MASK:0U)
          | ((uint32_t)(config->HardwareTriggerMode) << 4U)
          | DMA_CHANNEL_CFG_CHPRIORITY(config->ChannelPriority)
          ;
    if (NULL != (config->BurstTransferConfig))
    {
        tmp32 |= DMA_CHANNEL_CFG_TRIGBURST_MASK
               | DMA_CHANNEL_CFG_BURSTPOWER(config->BurstTransferConfig->BurstTransferSizePower)
               | (config->BurstTransferConfig->EnableSourceBurstWrap?DMA_CHANNEL_CFG_SRCBURSTWRAP_MASK:0U)
               | (config->BurstTransferConfig->EnableDestBurstWrap?DMA_CHANNEL_CFG_DSTBURSTWRAP_MASK:0U)
               ;
    }
    base->CHANNEL[channelIndex].CFG = tmp32;

#if 0
    /* CANNEL XFERCFG */
    tmp32 = (config->EnableReloadSetting ? DMA_CHANNEL_XFERCFG_RELOAD_MASK:0U)
          | (config->EnableTriggerNow ? DMA_CHANNEL_XFERCFG_SWTRIG_MASK:0U)
          | (config->EnableAutoClearHarewareTrigger ? DMA_CHANNEL_XFERCFG_CLRTRIG_MASK:0U)
          | (config->EnableFlagOnInterruptA ? DMA_CHANNEL_XFERCFG_SETINTA_MASK:0U)
          | (config->EnableFlagOnInterruptB ? DMA_CHANNEL_XFERCFG_SETINTB_MASK:0U)
          | DMA_CHANNEL_XFERCFG_WIDTH(config->TransferWidth)
          | DMA_CHANNEL_XFERCFG_SRCINC(config->TransferSourceAddressIncrease)
          | DMA_CHANNEL_XFERCFG_DSTINC(config->TransferDestAddressIncrease)
          | DMA_CHANNEL_XFERCFG_XFERCOUNT(config->TransferCount-1U)
          ;
    base->CHANNEL[channelIndex].XFERCFG = tmp32;
#endif
}


bool DMA_IsChannelDescriptorValid(DMA_Type *base, uint32_t channelIndex)
{
    return (0U != (DMA_CHANNEL_XFERCFG_CFGVALID_MASK & base->CHANNEL[channelIndex].XFERCFG));
}
/*
void DMA_SetSoftwareTrigger(DMA_Type *base, uint32_t channelIndex, bool enable)
{
    if (enable)
    {
        base->CHANNEL[channelIndex].CFG |= DMA_CHANNEL_CFG_SWTRIG_MASK;
    }
    else
    {
        base->CHANNEL[channelIndex].CFG &= ~DMA_CHANNEL_CFG_SWTRIG_MASK;
    }
}
*/

uint32_t DMA_CreateTransferConfigWord(DMA_TransferConfig_T *config)
{
    return (
        ((config->EnableValidNow)?DMA_CHANNEL_XFERCFG_CFGVALID_MASK:0U)
      | ((config->EnableReloadNextDescriptor)?DMA_CHANNEL_XFERCFG_RELOAD_MASK:0U)
      | ((config->EnableTriggerBySoftwareNow)?DMA_CHANNEL_XFERCFG_SWTRIG_MASK:0U)
      | ((config->EnableAutoClearTrigger)?DMA_CHANNEL_XFERCFG_CLRTRIG_MASK:0U)
      | ((config->EnableFlagOnInterruptA)?DMA_CHANNEL_XFERCFG_SETINTA_MASK:0U)
      | ((config->EnableFlagOnInterruptB)?DMA_CHANNEL_XFERCFG_SETINTB_MASK:0U)
      | DMA_CHANNEL_XFERCFG_WIDTH(config->TransferWidth)
      | DMA_CHANNEL_XFERCFG_SRCINC(config->TransferSourceAddressIncrease)
      | DMA_CHANNEL_XFERCFG_DSTINC(config->TransferDestAddressIncrease)
      | DMA_CHANNEL_XFERCFG_XFERCOUNT(config->TransferCount-1U)
        );
}

void DMA_SetHeadTransferDescriptor(DMA_Type *base, uint32_t channelIndex, DMA_TransferDescriptor_T *descriptor)
{
    DMA_TransferDescriptor_T *transferDescrpiter = (DMA_TransferDescriptor_T *)(base->SRAMBASE);

    transferDescrpiter += channelIndex;

    /* Copy the setting into descriptor table. */
    transferDescrpiter->TransferConfigWord = descriptor->TransferConfigWord;
    transferDescrpiter->SourceEndAddrress = descriptor->SourceEndAddrress;
    transferDescrpiter->DestEndAddress = descriptor->DestEndAddress;
    transferDescrpiter->LinkToNext = descriptor->LinkToNext;

    /* Setup the XFERCFG register for the first transfer. */
    base->CHANNEL[channelIndex].XFERCFG = descriptor->TransferConfigWord;
}

/* 附加传输描述符，直接链接传入描述符的结构体
 * 描述符指定内存必须为静态内存区域 */
void DMA_AddTransferDescriptor(DMA_Type *base, uint32_t channelIndex, DMA_TransferDescriptor_T *descriptor)
{
    DMA_TransferDescriptor_T *transferDescrpiter = (DMA_TransferDescriptor_T *)(base->SRAMBASE);
    transferDescrpiter += channelIndex;

    /* 遍历至最后一个描述符 */
    while (transferDescrpiter->LinkToNext)
    {
        transferDescrpiter = transferDescrpiter->LinkToNext;
    }
    transferDescrpiter->LinkToNext = descriptor;
}

/* 将用户传入的起始地址结合传输配置中设定的传输数量和位宽转换为结束地址
 * 用户可以根据手册直接配置传输描述符中值，或者借助于本函数进行计算
 */
void DMA_ConvertTransferDescriptor(DMA_TransferDescriptor_T *descriptor)
{
    uint32_t incMult;
    uint32_t transferCount = (descriptor->TransferConfigWord & DMA_CHANNEL_XFERCFG_XFERCOUNT_MASK) >> DMA_CHANNEL_XFERCFG_XFERCOUNT_SHIFT;
    uint32_t transferWidth = (descriptor->TransferConfigWord & DMA_CHANNEL_XFERCFG_WIDTH_MASK) >> DMA_CHANNEL_XFERCFG_WIDTH_SHIFT;
    transferWidth = (transferWidth == eDMA_TransferWidth_8b ) ? 1U : (
                    (transferWidth == eDMA_TransferWidth_16b) ? 2U : (
                    (transferWidth == eDMA_TransferWidth_32b) ? 4U : 0U ) );

    /* 源地址 */
    incMult = (descriptor->TransferConfigWord & DMA_CHANNEL_XFERCFG_SRCINC_MASK) >> DMA_CHANNEL_XFERCFG_SRCINC_SHIFT;
    incMult = (incMult == eDMA_TransferAddressIncrease_NoChange) ? 0U : (
              (incMult == eDMA_TransferAddressIncrease_1xWidth)  ? 1U : (
              (incMult == eDMA_TransferAddressIncrease_2xWidth)  ? 2U : (
              (incMult == eDMA_TransferAddressIncrease_4xWidth)  ? 4U : 0U) ) );
    descriptor->SourceEndAddrress = descriptor->SourceStartAddress
                                  + transferWidth * incMult * transferCount;
    /* 目的地址 */
    incMult = (descriptor->TransferConfigWord & DMA_CHANNEL_XFERCFG_DSTINC_MASK) >> DMA_CHANNEL_XFERCFG_DSTINC_SHIFT;
    incMult = (incMult == eDMA_TransferAddressIncrease_NoChange) ? 0U : (
              (incMult == eDMA_TransferAddressIncrease_1xWidth)  ? 1U : (
              (incMult == eDMA_TransferAddressIncrease_2xWidth)  ? 2U : (
              (incMult == eDMA_TransferAddressIncrease_4xWidth)  ? 4U : 0U) ) );
    descriptor->DestEndAddress = descriptor->DestStartAddress
                                  + transferWidth * incMult * transferCount;
}

/* EOF. */

